package dfsketches.earth_crust_gen;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import dfsketches.bicycles.SparseVoxelOctotree;
import dfsketches.mineral_generator.BoundingBox;
import dfsketches.mineral_generator.MineralGenerator;
import dfsketches.mineral_generator.VolumeMineralGenerator;
import dfsketches.terrain_generator.ComplexCosineGenerator;
import dfsketches.terrain_generator.MountainedGenerator;
import dfsketches.visualizer_2d.VisualizerWindow;

public class PsiCrustGen {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		int size = 256;
		
		int[][] granite_level = genSurface(size, 150f, 20f, 5f, false);
		System.out.println("Granite formed.");
		
		int[][][] bm = new int[size][size][size];
		for (int i = 0; i < size; i++)
			for (int j = 0; j < size; j++) {
				Arrays.fill(bm[i][j], 0, granite_level[i][j], 0xFF808080);
				Arrays.fill(bm[i][j], granite_level[i][j], size, 0xFF505050);
			}
		MineralGenerator vg = new VolumeMineralGenerator(bm);

		int veinN = 50;
		Random rnd = new Random();
		int[] vein_colors = {0xFFFFFF00, 0xFF7f7f00, 0xFF00ff00};
		int vein_size = 50;
		for (int i = 0; i < veinN; i++) {
			int x = 100;		//TEST!!!!
			int y = rnd.nextInt(size-vein_size);
			int z = rnd.nextInt(size-vein_size);
			BoundingBox box = new BoundingBox(x, y, z, vein_size, vein_size, vein_size);
			vg.addVein(5, 30, 5, box, vein_colors[rnd.nextInt(3)]);
		}
		System.out.println("Veins added.");
		
		vg.addFullCluster(5, 20, 40, new BoundingBox(64, 10, 20, 128, 128, 128), 0xFFa0a0a0);
		vg.addFullCluster(5, 20, 40, new BoundingBox(64, 80, 40, 128, 128, 128), 0xFF00FFFF);
		vg.addFullCluster(5, 20, 40, new BoundingBox(64, 20, 110, 128, 128, 128), 0xFFBFBF00);
		vg.addFullCluster(5, 20, 40, new BoundingBox(64, 110, 80, 128, 128, 128), 0xFF3FBFa0);
		vg.addFullCluster(5, 20, 40, new BoundingBox(64, 120, 120, 128, 128, 128), 0xFF105010);
		vg.addFullCluster(5, 20, 40, new BoundingBox(64, 120, 10, 128, 128, 128), 0xFFc0c0c0);
		vg.addFullCluster(5, 20, 40, new BoundingBox(64, 50, 70, 128, 128, 128), 0xFFa05050);
		vg.addFullCluster(5, 20, 40, new BoundingBox(64, 80, 90, 128, 128, 128), 0xFFf000d0);
		System.out.println("Minerals added.");
		
		int[][] magma_level = genSurface(size, 20f, 20f, 5f, false);
		System.out.println("Magma formed.");
		
		MountainedGenerator terra_gen = new MountainedGenerator();
		Map<String, Object> terra_param = new HashMap<String, Object>();
		terra_param.put("init_height", 150f);
		terra_param.put("magnitude", 30f);
		terra_param.put("magnitude_k", 2.1f);
		terra_param.put("mountain_k", 1f);
		int[][] terra_level = terra_gen.generate(size, terra_param);
		System.out.println("Terrain formed.");
		
		int soil_min_h = 150;
		int soil_max_h = 180;
		float soil_k = 0.1f;
		int soil_default = (int)((soil_max_h - soil_min_h) * soil_k);
		
		SparseVoxelOctotree vox = new SparseVoxelOctotree(size);
		vox.put(0,0,0, size-1,size-1,size-1, 0xFF40B0B0);
		for (int x = 0; x < size; x++) {
			for (int y = 0; y < size; y++) {
				int ml = magma_level[x][y];
				for (int z = 0; z < ml; z++)
					vox.put(x, y, z, 0xFFA04040);

				int tl = Math.min(terra_level[x][y], size);
				float k = 1f / (tl - ml);
				for (int z = ml; z < tl; z++)
					vox.put(x, y, z, bm[x][y][Math.min((int)(Math.pow(1-(z - ml)*k, 1.5f) * size), size-1)]);
				
				int sl = tl + ((tl>=soil_min_h) ? (int)((soil_max_h-tl)*soil_k) : soil_default);
				for (int z = tl; z < sl; z++)
					vox.put(x, y, z, 0xFF404000);
			}
		}
		System.out.println("Voxels filled.");
		
		int[][] bitmap = new int[size][size];
		int sliceX = 128;
		for (int y = 0; y < size; y++)
			for (int z = 0; z < size; z++)
				bitmap[size-1 - z][y] = vox.get(sliceX, y, z);
		VisualizerWindow w = new VisualizerWindow(bitmap);
		w.setLocation(200, 200);
		w.draw();
	}
	
	private static int[][] genSurface(int size, float init_height, float magnitude, float magnitude_k,
			boolean fixed_borders) {
		ComplexCosineGenerator gen = new ComplexCosineGenerator();
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("init_height", init_height);
		param.put("magnitude", magnitude);
		param.put("magnitude_k", magnitude_k);
		param.put("fixed_borders", fixed_borders);
		return gen.generate(size, param);
	}
	
}